#include <uC++.h>

#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>				
#include <time.h>
#include <string>

#include "Global.h"
#include "config.h"
#include "Printer.h"
#include "Bank.h"
#include "Parent.h"
#include "Student.h"
#include "WATCardOffice.h"
#include "VendingMachine.h"
#include "BottlingPlant.h"
#include "NameServer.h"

using namespace std;					// direct access to std

bool convert( int &val, char *buffer ) 
{		// convert C string to integer
    std::stringstream ss( buffer );			// connect stream and buffer
    ss >> dec >> val;					// convert integer from buffer
    return ! ss.fail() &&				// conversion successful ?
	// characters after conversion all blank ?
	string( buffer ).find_first_not_of( " ", ss.tellg() ) == string::npos;
} // convert

void usage( char *argv[] ) 
{
   cout << "Usage: " << argv[0] << " [ config-file [ random-seed (> 0) ] ]" << endl;
   exit( EXIT_FAILURE );				// TERMINATE
} // usage

void uMain::main() 
{
	int seed = time(NULL);
	string file_name = "soda.config";
	ConfigParms main_config;

	switch ( argc ) 
	{
		case 3:
			if ( ! convert( seed, argv[2] ) || seed <= 0 ) {	// invalid integer ?
				usage( argv );
			} // if
			// FALL THROUGH
		case 2:
			file_name = string(argv[1]);
			break;
		case 1:
			break;
		default:						// wrong number of options
			usage( argv );
	} // switch
	
	Global::prng.seed(seed);
	// process config file
	processConfigFile(file_name.c_str(), main_config);
	// create printer
	Printer		main_printer(main_config.numStudents, 
							 main_config.numVendingMachines,
							 main_config.numCouriers);
	// create bank
	Bank        main_bank(main_config.numStudents);
	// create parent
	Parent*     main_parent = new Parent(main_printer, main_bank,
	                        main_config.numStudents, 
	                        main_config.parentalDelay);
	// create WATCardOffice
	WATCardOffice* main_office = new WATCardOffice(main_printer, main_bank, 
												   main_config.numCouriers);
	// create NameServer
	NameServer* main_server = new NameServer(main_printer,
											 main_config.numVendingMachines, 
											 main_config.numStudents);
	// create vending machines
	VendingMachine* main_machines[main_config.numVendingMachines];
	for(unsigned int i=0; i<main_config.numVendingMachines; i++)
	{
		main_machines[i] = new VendingMachine(main_printer,
											  *main_server,
											  i,
											  main_config.sodaCost,
											  main_config.maxStockPerFlavour);
	}
	// create bottling plant
	BottlingPlant* main_plant = new BottlingPlant(main_printer, 
												  *main_server,
												  main_config.numVendingMachines,
												  main_config.maxShippedPerFlavour,
												  main_config.maxStockPerFlavour,
												  main_config.timeBetweenShipments);
	//create students
	Student* main_students[main_config.numStudents];
	for(unsigned int i=0; i<main_config.numStudents; i++)
	{
		main_students[i] = new Student(main_printer,
									   *main_server,
									   *main_office,
									   i, 
									   main_config.maxPurchases);
	}

	for(unsigned int i=0; i<main_config.numStudents; i++) delete main_students[i];
	delete main_plant;
	for(unsigned int i=0; i<main_config.numVendingMachines; i++) delete main_machines[i];
	delete main_server;
	delete main_office;
    delete main_parent;


#ifdef DEBUG_
	cout << "Seed = " << seed << endl;
	cout << "Config file name = " << file_name;
	cout << "------------------------config values--------------------------" << endl;
    cout << "sodaCost = " << main_config.sodaCost << endl;                      
    cout << "numStudents = " << main_config.numStudents << endl;                     
    cout << "maxPurchases = " <<  main_config.maxPurchases << endl;                  
    cout << "numVendingMachines = " << main_config.numVendingMachines << endl;            
    cout << "maxStockPerFlavour = " << main_config.maxStockPerFlavour << endl;           
    cout << "maxShippedPerFlavour = " << main_config.maxShippedPerFlavour << endl;          
    cout << "timeBetweenShipments = " << main_config.timeBetweenShipments << endl;          
    cout << "parentalDelay = " << main_config.parentalDelay << endl;                 
    cout << "numCouriers = " << main_config.numCouriers << endl; 
#endif
} // uMain::main
